#ifndef _M_SESSION_H__
#define _M_SESSION_H__
#include "util.hpp"
#include <unordered_map>

typedef enum{UNLOGIN,LOGIN} ss_statu;

class session
{
    private:
        uint64_t _ssid;
        uint64_t _uid;
        ss_statu _statu;
        wsserver_t::timer_ptr _tp;
    public:
        using ptr = std::shared_ptr<session>;
        session(uint64_t ssid):_ssid(ssid) { DLOG("session %p is created!",this); }
        ~session() { DLOG("session %p is destroyed!",this); }

        uint64_t ssid() { return _ssid; }
        void set_statu(ss_statu statu) { _statu = statu; }
        void set_user(uint64_t uid) { _uid = uid; }
        uint64_t get_user() { return _uid; }
        bool is_login() { return (_statu == LOGIN); }
        void set_timer_ptr(const wsserver_t::timer_ptr &tp) { _tp = tp;}
        wsserver_t::timer_ptr& get_timer() { return _tp; }

};

class session_manager
{
    private:
        uint64_t _next_ssid;
        std::mutex _mutex;
        std::unordered_map<uint64_t,session::ptr> _session;
        wsserver_t* _server;
    public:
        #define SESSION_TIMEOUT 30000
        #define SESSION_FOREVER -1
        session_manager(wsserver_t* srv):_next_ssid(1),_server(srv)
        {
            DLOG("session manager is initialized!");
        }

        ~session_manager()
        {
            DLOG("session manager is destroyed!");
        }

        session::ptr create_session(uint64_t uid,ss_statu statu)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            session::ptr ssp = std::make_shared<session>(_next_ssid);
            ssp->set_statu(statu);
            ssp->set_user(uid);
            _session.insert(std::make_pair(_next_ssid,ssp));
            _next_ssid++;
            return ssp;
        }

        session::ptr get_session_by_ssid(uint64_t ssid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _session.find(ssid);
            if(it == _session.end())
            {
                return session::ptr();
            }
            return it->second;
        }

        void remove_session(uint64_t ssid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _session.erase(ssid);
        }

        void append_session(const session::ptr &ssp) 
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _session.insert(std::make_pair(ssp->ssid(), ssp));
        }

        void set_session_expire_time(uint64_t ssid,int sec)
        {
            // 依赖于websocketpp里的定时器来完成session生命周期的管理
            // 在http通信的时候（登录、注册）session应该具备声明周期，指定时间后删除
            // 但客户端建立websocket长连接后，session应该是永久存在的
            // 登录之后，创建session，此时需要指定时间无通信后删除
            // 但是进入游戏大厅或房间后就要重新设置为永久，退出后重新设置为临时

            session::ptr ssp = get_session_by_ssid(ssid);
            if(ssp.get() == nullptr)
                return;
            wsserver_t::timer_ptr tp = ssp->get_timer();
            if(tp.get() == nullptr && sec == SESSION_FOREVER)
            {
                // 1.在session永久的情况下，设置为永久存在
                return;
            }
            else if(tp.get() == nullptr && sec == SESSION_TIMEOUT)
            {
                // 2.在session永久的情况下，设置指定时间后删除的定时任务
                wsserver_t::timer_ptr tmp_tp = _server->set_timer(sec,
                std::bind(&session_manager::remove_session,this,ssid));
                ssp->set_timer_ptr(tmp_tp);
            }
            else if(tp.get() != nullptr && sec == SESSION_FOREVER)
            {
                // 3.在session设置了定时删除的情况下，将session设置为永久存在
                // 取消定时任务后，会直接执行定时结束后的任务，因此需要重新添加session
                tp->cancel(); // 该操作不会立即取消，可能会导致后续语句先执行再取消
                ssp->set_timer_ptr(wsserver_t::timer_ptr());
                // 所以这里不能直接：_session.insert(std::make_pair(ssp->ssid(),ssp));
                _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));
            }
            else if (tp.get() != nullptr && sec != SESSION_FOREVER)
            {
                // 4. 在session设置了定时删除的情况下，将session重置删除时间
                tp->cancel();//因为这个取消定时任务并不是立即取消的
                ssp->set_timer_ptr(wsserver_t::timer_ptr());
                _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));

                //重新给session添加定时销毁任务
                wsserver_t::timer_ptr tmp_tp  = _server->set_timer(sec, 
                    std::bind(&session_manager::remove_session, this, ssp->ssid()));
                //重新设置session关联的定时器
                ssp->set_timer_ptr(tmp_tp);
            }
        }
};


#endif